// created by william
// first application under playbook

package
{
//	import flash.display.Shape;
//	import SizeDialog.*;
	
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageOrientation;
	import flash.display.StageScaleMode;
	import flash.events.*;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.sensors.Accelerometer;
	import flash.ui.Multitouch;
	import flash.ui.MultitouchInputMode;
	import flash.utils.Timer;
	
	import qnx.events.*;
	import qnx.system.*;
	import qnx.ui.buttons.LabelButton;
	import qnx.ui.core.Container;
	import qnx.ui.core.ContainerAlign;
	import qnx.ui.core.ContainerFlow;
	import qnx.ui.core.SizeMode;
	import qnx.ui.core.SizeUnit;
	import qnx.ui.core.Spacer;
	import qnx.ui.display.Image;
	import qnx.ui.events.SliderEvent;
	import qnx.ui.slider.Slider;
	import qnx.ui.text.Label;
	
//	// The following metadata specifies the size and properties of the canvas that
//	// this application should occupy on the BlackBerry PlayBook screen.
//	[SWF(width="1024", height="600", backgroundColor="#cccccc", frameRate="30")]
	
	public class LifeGame extends Sprite
	{
//		static var mainGame:LifeGame;
		private const CELL_SIZE_MIN:int = 10;
		private const CELL_SIZE_MAX:int = 50;
		
		public var landscapeMode:Boolean;
		private var cellSize:uint = 20;	// 20*20 per cell
		private var oldCellSize:uint = 20;
		public var world:World;
		private var worldSprite:Sprite;
		private var gameTimer:Timer;
		private var generateRate:int;
		private var speedLabel:Label;
		private var playButton:LabelButton;
		private var generationLabel:Label;
		
		private var sizeDialog:SizeDialog;
		private var showSizeDialog:Boolean = false;
		
		public static var bLevel:int;
		public static var bState:int;
		
		private var myMain:Container;
		private var worldSub:Container;
		private var menuSub:Container;
		
		private var zoomX:Number;
		private var zoomY:Number;
		private var worldCenter:Point;
		
		private var bgImage:Image;
		private var bgImage2:Image;
		
		public function LifeGame()
		{
//			mainGame = this;
			trace("start");
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			landscapeMode = (stage.orientation == StageOrientation.DEFAULT);
			
			world = new World();
			worldCenter = new Point(0,0);
			
			addEventListener(Event.ADDED_TO_STAGE, handleAddToStage);
			initializeUI();
			
			
			gameTimer = new Timer(1000, 0);
			gameTimer.delay = 200;
			gameTimer.addEventListener(TimerEvent.TIMER, onTick);
			gameTimer.addEventListener(TimerEvent.TIMER_COMPLETE, timerComplete);
			
			//stage.addEventListener(MouseEvent.CLICK, modifyCell);
			
			initializeDeviceEvent();
			sizeDialog = new SizeDialog(this);
			
			//add for zoom gesture
			Multitouch.inputMode = MultitouchInputMode.GESTURE;
			worldSub.addEventListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);
			worldSub.addEventListener(TransformGestureEvent.GESTURE_PAN, onPan);
			
			stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGE, onOrientationChange);
		}
		
		public function handleAddToStage(evt:Event):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, handleAddToStage);
			addEventListener(Event.RESIZE, onResize);
			onResize(new Event(Event.RESIZE));
		}
		
		private function onResize(evt:Event):void
		{
			trace("resize");
			resizeCanvas();
		}
		
		private function resizeCanvas():void
		{
//			myMain.setSize(stage.stageWidth, stage.stageHeight);
			//main there is a bug about stage.fullScreen, so use number directly
			if(landscapeMode)
				myMain.setSize(1024, 600);
			else
				myMain.setSize(600, 1024);
		}
		
		public function initializeDeviceEvent():void
		{
			addEventListener(Event.ACTIVATE, onActivate);
			addEventListener(Event.DEACTIVATE, onDeactivate);
			
			addEventListener(QNXApplicationEvent.LOW_MEMORY, onLowMemory);
			Device.device.batteryMonitoringEnabled = true;
			bLevel = Device.device.batteryLevel;
			bState = Device.device.batteryState;
			addEventListener(DeviceBatteryEvent.LEVEL_CHANGE, onLevelChange);
			addEventListener(DeviceBatteryEvent.STATE_CHANGE, onStateChange);
		}
		
		public function onActivate(event:Event):void
		{
			// todo, save state
		}
		
		public function onDeactivate(event:Event):void
		{
			//todo, reload state
			if(gameTimer.running)
			{
				pauseGame();
			}
		}
		
		public function onLowMemory(event:Event):void
		{
			//todo, save state
		}
		
		public function onLevelChange(evt:DeviceBatteryEvent):void
		{
			bLevel = evt.batteryLevel;
			evaluate();
		}
		
		public function onStateChange(evt:DeviceBatteryEvent):void
		{
			bState = evt.batteryState;
			evaluate();
		}
		
		public function evaluate():void
		{
			if(bState == 3 || bLevel < 10)
			{
				//save state
				//displayWarning();
				pauseGame();
			}
		}
		
		public function initializeUI():void
		{
			bgImage = new Image();
			bgImage.setImage("../goddess1.png");
			bgImage2 = new Image();
			bgImage2.setImage("../goddess2.png");
			bgImage2.y = 10;
			if(landscapeMode)
				bgImage2.visible = false;
			else
				bgImage.visible = false;
			addChild(bgImage);
			addChild(bgImage2);
			
			//main container
			myMain = new Container();
//			myMain.margins = Vector.<Number>([20,20,20,20]);
//			myMain.debugColor = 0xFF0000;
			myMain.flow = ContainerFlow.VERTICAL;
			addChild(myMain);
			
			//world container & menu container
			worldSub = new Container();
			worldSub.flow = ContainerFlow.HORIZONTAL;
			worldSub.padding = 5;
			worldSub.size = 91;
			worldSub.sizeUnit = SizeUnit.PERCENT;
			worldSub.sizeMode = SizeMode.BOTH;
//			worldSub.margins = Vector.<Number>([10,10,10,10]);
//			worldSub.debugColor = 0x00FF00;
			worldSub.align = ContainerAlign.MID;
			menuSub = new Container();
			menuSub.flow = ContainerFlow.HORIZONTAL;
			if(!landscapeMode)
				menuSub.flow = ContainerFlow.VERTICAL;
			menuSub.padding = 5;
			menuSub.size = 9;
			menuSub.sizeUnit = SizeUnit.PERCENT;
			menuSub.align = ContainerAlign.MID;//defalt value
//			menuSub.margins = Vector.<Number>([10,10,10,10]);
//			menuSub.debugColor = 0x00FF00;
			
			
			//menu buttons
			//add two sub container
			var menuSub1:Container = new Container();
			menuSub1.flow = ContainerFlow.HORIZONTAL;
			menuSub1.size = 50;
			menuSub1.sizeUnit = SizeUnit.PERCENT;
//			menuSub1.debugColor = 0xFF;
//			menuSub1.align = ContainerAlign.MID;
			
			var menuSub2:Container = new Container();
			menuSub2.flow = ContainerFlow.HORIZONTAL;
			menuSub2.size = 50;
			menuSub2.sizeUnit = SizeUnit.PERCENT;
//			menuSub2.debugColor = 0xFF;
//			menuSub2.align = ContainerAlign.MID;
			
			
			generationLabel = new Label();
			generationLabel.text = "gen:"+world.lifeGeneration();
			generationLabel.height = 30;
			generationLabel.size = 80;
			generationLabel.sizeUnit = SizeUnit.PIXELS;
//			generationLabel.format.align = TextFormatAlign.CENTER;
			
			var speedSlider:Slider = new Slider();
			speedSlider.minimum = 1;
			speedSlider.maximum = 20;
			speedSlider.height = 20;
			speedSlider.size = 200;
			speedSlider.value = 5;
			speedSlider.sizeUnit = SizeUnit.PIXELS;
			speedSlider.addEventListener(SliderEvent.MOVE, speedChange);
			
			speedLabel = new Label();
			speedLabel.text = "fps:"+"5";
			speedLabel.height = generationLabel.height;
			speedLabel.size = 60;
			speedLabel.sizeUnit = SizeUnit.PIXELS;
			
			playButton = new LabelButton();
			playButton.label = "Play";
			playButton.height = 40;
			playButton.size = 80;
			playButton.sizeUnit = SizeUnit.PIXELS;
			
			var randomButton:LabelButton = new LabelButton();
			randomButton.label = "Random";
			randomButton.height = playButton.height;
			randomButton.size = playButton.size;
			randomButton.sizeUnit = SizeUnit.PIXELS;
			
			var clearButton:LabelButton = new LabelButton();
			clearButton.label = "Clear";
			clearButton.height = playButton.height;
			clearButton.size = playButton.size;
			clearButton.sizeUnit = SizeUnit.PIXELS;
			
			var sizeButton:LabelButton = new LabelButton();
			sizeButton.label = "size";
			sizeButton.height = playButton.height;
			sizeButton.size = playButton.size;
			sizeButton.sizeUnit = SizeUnit.PIXELS;
			
			menuSub1.addChild(new Spacer());
			menuSub1.addChild(generationLabel);
			menuSub1.addChild(new Spacer());
			menuSub1.addChild(speedLabel);
			menuSub1.addChild(speedSlider);
			menuSub1.addChild(new Spacer());
			
			menuSub2.addChild(new Spacer());
			menuSub2.addChild(playButton);
			menuSub2.addChild(new Spacer(10, SizeUnit.PIXELS));
			menuSub2.addChild(randomButton);
			menuSub2.addChild(new Spacer(10, SizeUnit.PIXELS));
			menuSub2.addChild(clearButton);
			menuSub2.addChild(new Spacer(10, SizeUnit.PIXELS));
			menuSub2.addChild(sizeButton);
			menuSub2.addChild(new Spacer());
			
			menuSub.addChild(menuSub1);
			menuSub.addChild(menuSub2);
			
			//world container
			worldSprite = new Sprite();
//			worldSprite.graphics.lineStyle(1, 0x0, 1.0);
			worldSub.addChild(new Spacer());
			worldSub.addChild(worldSprite);
			worldSub.addChild(new Spacer());
			drawWorld();
			
			//add sub container
			myMain.addChild(worldSub);
			myMain.addChild(menuSub);
			
			worldSprite.addEventListener(MouseEvent.CLICK, modifyCell);
			playButton.addEventListener(MouseEvent.CLICK, playGame);
			randomButton.addEventListener(MouseEvent.CLICK, randomGame);
			clearButton.addEventListener(MouseEvent.CLICK, clearGame);
			sizeButton.addEventListener(MouseEvent.CLICK, changeSize);
			
		}
		
		public function speedChange(event:SliderEvent):void
		{
			generateRate = event.value;
//			trace("speed = ", generateRate);
//			if(gameTimer.running == false)
			{
				gameTimer.delay = 1000/generateRate;
				speedLabel.text = "fps:"+generateRate;
			}
		}
		public function modifyCell(event:MouseEvent):void
		{
			var column:int = event.localX / cellSize;
			var row:int = event.localY / cellSize;
			if(column >=0 && column < world.columns && row >=0 && row < world.rows)
			{
				trace("modify cell function");
				world.SetLifeState(row, column, !world.lifeState(row, column));
				drawWorld();
			}
		}
		
		public function drawWorld():void
		{
			//clip should be (10,10,1004,540)
			var i:int, j:int;
			var startPosX:int = 0;
			var startPosY:int = 0;
			var endPosX:int = world.columns * cellSize;
			var endPosY:int = world.rows * cellSize;
			
			var widthLimit:int = 1004;
			var heightLimit:int = 526;
			if(!landscapeMode)
			{
				widthLimit = 580;
				heightLimit = 896;
			}
			
			if(endPosX >= widthLimit)
			{
				startPosX = (endPosX - widthLimit)/2 + worldCenter.x;
				endPosX = startPosX+widthLimit;
				
				if(startPosX < 0)
				{
					startPosX = 0;
					endPosX = widthLimit;
					worldCenter.x = -(world.columns * cellSize-widthLimit)/2;
				}
				
				if(endPosX > world.columns*cellSize)
				{
					endPosX = world.columns*cellSize;
					startPosX = endPosX - widthLimit;
					worldCenter.x = (world.columns * cellSize - widthLimit)/2;
				}
			}
			else
				worldCenter.x = 0;
			
			if(endPosY >= heightLimit)
			{
				startPosY = (endPosY - heightLimit)/2 + worldCenter.y;
				endPosY = startPosY + heightLimit;
				
				if(startPosY < 0)
				{
					startPosY = 0;
					endPosY = heightLimit;
					worldCenter.y = -(world.rows*cellSize-heightLimit)/2;
				}
				
				if(endPosY > world.rows*cellSize)
				{
					endPosY = world.rows*cellSize;
					startPosY = endPosY - heightLimit;
					worldCenter.y = (world.rows*cellSize-heightLimit)/2;
				}
			}
			else
				worldCenter.y = 0;
			
			var worldW:int = endPosX - startPosX;
			var worldH:int = endPosY - startPosY;
			const BG_ALPHA:Number = 0.6;
			const FG_ALPHA:Number = 0.8;
			
			worldSprite.graphics.clear();
			worldSprite.graphics.beginFill(0x808080, BG_ALPHA);
//			worldSprite.graphics.drawRect(0, 0, endPosX, endPosY);
			worldSprite.graphics.drawRect(startPosX, startPosY, worldW, worldH);
			worldSprite.graphics.endFill();
			
			// add this to make sure the graphic size right;
			worldSprite.graphics.beginFill(0xff0000, FG_ALPHA);
			worldSprite.graphics.lineStyle(1,0x0,0);
			worldSprite.graphics.moveTo(0,0);
			worldSprite.graphics.lineTo(world.columns * cellSize + worldCenter.x*2, world.rows * cellSize+worldCenter.y*2);
//			worldSprite.graphics.lineTo(worldW, worldH+worldCenter.y*2);
			worldSprite.graphics.endFill();
			
			//draw cells
			var cellW:int = 0;
			var cellH:int = 0;
			var cellX:int = 0;
			var cellY:int = 0;
			
			worldSprite.graphics.beginFill(0x00ff00, FG_ALPHA);
			
			var tx:int = 0;
			var ty:int = 0;
			var si:int = startPosY/cellSize;
			var sj:int = startPosX/cellSize;
			var tsize:int = cellSize-2;
			for(i = si; i<world.rows; i++)
			{
				ty = i*cellSize;
				if(ty > endPosY)
					break;
				for(j=sj; j<world.columns; j++)
				{
					tx = j*cellSize;
					if(tx > endPosX)
						break;
					if(world.lifeState(i, j))
					{
//						cellX = j*cellSize+1;
//						cellY = i*cellSize+1;
//						if(j*cellSize+cellSize-1>endPosX)
//							cellW = endPosX-j*cellSize-1;
//						else if(cellX < startPosX)
//						{
//							cellW = cellX+cellSize-startPosX-2;
//							cellX = startPosX;
//						}
//						else
//							cellW = cellSize-2;
//						
//						if(i*cellSize+cellSize-1>endPosY)
//							cellH = endPosY-i*cellSize-1;
//						else if(cellY < startPosY)
//						{
//							cellH = cellY+cellSize-startPosY-2;
//							cellY = startPosY;
//						}
//						else
//							cellH = cellSize-2;
						worldSprite.graphics.drawRect(tx+1, ty+1, tsize, tsize);
//						worldSprite.graphics.drawRect(cellX, cellY, cellW, cellH);
					}
				}
			}
			worldSprite.graphics.endFill();
			
			worldSprite.graphics.beginFill(0xff0000);
			worldSprite.graphics.lineStyle(2, 0x000000,1.0);
			//draw rows;
			for(i=startPosY/cellSize; i<=world.rows; i++)
			{
				ty = i*cellSize;
				if(ty > endPosY)
					break;
				worldSprite.graphics.moveTo(startPosX, ty);
//				worldSprite.graphics.moveTo(0, i*cellSize);
				worldSprite.graphics.lineTo(endPosX, ty);
			}
			//draw columns;
			for(i=startPosX/cellSize; i<=world.columns; i++)
			{
				tx = i*cellSize;
				if(tx > endPosX)
					break;
				worldSprite.graphics.moveTo(tx, startPosY);
				worldSprite.graphics.lineTo(tx, endPosY);
			}
			
			worldSprite.graphics.endFill();
			// Draw a circle and add it to the display 
			var worldMask:Sprite = new Sprite();
			worldMask.graphics.lineStyle(1, 0x000000);
			worldMask.graphics.beginFill(0x0000ff, FG_ALPHA);
			worldMask.graphics.drawRect(10, 10, widthLimit, heightLimit);
			worldMask.graphics.endFill();
			worldSprite.mask = worldMask;
			
			//set generation 
			generationLabel.text = "gen:"+world.lifeGeneration();
		}
		
		public function playGame(event:MouseEvent):void
		{
			trace("press play");
			if(gameTimer.running)
			{
				//stop game
				playButton.label = "Play";
				gameTimer.stop();
			}
			else
			{
				//start game
				playButton.label = "Pause";
				gameTimer.start();
			}
		}
		
		public function pauseGame():void
		{
			if(gameTimer.running)
			{
				playButton.label = "Play";
				gameTimer.stop();
			}
		}
		
		public function randomGame(event:MouseEvent):void
		{
			world.randomWorld();
			drawWorld();
		}
		
		public function clearGame(event:MouseEvent):void
		{
			pauseGame();
			world.clearWorld();
			drawWorld();
		}
		
		public function changeSize(event:MouseEvent):void
		{
			addChild(sizeDialog);
			sizeDialog.drawNow();
			showSizeDialog = true;
			pauseGame();
		}
		
		public function OKClicked(e:MouseEvent):void
		{
			sizeDialog.changeSizeNumber();
			world.resizeWorld(sizeDialog.getWidthNumber(), sizeDialog.getHeightNumber());
			
			removeChild(sizeDialog);
			drawWorld();
			worldSub.layout();
			showSizeDialog = false;
		}
		
		public function cancelDialog(e:MouseEvent):void
		{
			removeChild(sizeDialog);
			showSizeDialog = false;
		}
		
		public function onTick(event:TimerEvent):void
		{
			if(gameTimer.running)
			{
				world.UpdateWorld();
				drawWorld();
			}
		}
		
		public function timerComplete(event:TimerEvent):void
		{
			playButton.label = "Play";
		}
		
		private function onZoom(event:TransformGestureEvent):void
		{
//			trace("x:"+event.scaleX);
//			trace("y:"+event.scaleY);
			if(showSizeDialog == true)
				return;
			
			if(event.phase == GesturePhase.BEGIN){
//				trace("begin");
				zoomX = 1.0;
				zoomY = 1.0;
				oldCellSize = cellSize;
			}
			else if(event.phase == GesturePhase.UPDATE){
				//update current world
//				trace("update");
				zoomX *= event.scaleX;
				zoomY *= event.scaleY;
				var zoom:Number = Math.sqrt(zoomX*zoomY);
				var newSize:uint = uint(zoom*oldCellSize);
				if(newSize < CELL_SIZE_MIN)
					newSize = CELL_SIZE_MIN;
				else if(newSize >CELL_SIZE_MAX)
					newSize = CELL_SIZE_MAX;
				
				if(newSize != cellSize)
				{
					cellSize = newSize;
					drawWorld();
					worldSub.layout();
				}
			}
			else if(event.phase == GesturePhase.END){
//				trace("end");
				oldCellSize = cellSize;
				drawWorld();
				worldSub.layout();
			}
		}
		
		private function onPan(event:TransformGestureEvent):void
		{
			if(showSizeDialog == true)
				return;

			if(event.phase == GesturePhase.BEGIN){
//				trace("pan begin");
			}
			else if(event.phase == GesturePhase.UPDATE){
//				trace("pan update");
				worldCenter.x -= event.offsetX;
				worldCenter.y -= event.offsetY;
				drawWorld();
				worldSub.layout();
			}
			else if(event.phase == GesturePhase.END){
//				trace("pan end")
			}
//			trace("lx:"+event.localX+"  ly:"+event.localY);
//			trace("ox:"+event.offsetX+"  oy"+event.offsetY);
		}
		
		private function onOrientationChange(event:StageOrientationEvent):void
		{
			switch(event.afterOrientation)
			{
				case StageOrientation.ROTATED_LEFT:
				case StageOrientation.ROTATED_RIGHT:
					//portrait
					landscapeMode = false;
					resizeCanvas();
					menuSub.flow = ContainerFlow.VERTICAL;
					myMain.layout();
					bgImage.visible = false;
					bgImage2.visible = true;
					if(showSizeDialog == true)
						sizeDialog.drawNow();
					break;
				case StageOrientation.DEFAULT:
					//landscape
					landscapeMode = true;
					resizeCanvas();
					menuSub.flow = ContainerFlow.HORIZONTAL;
					myMain.layout();
					bgImage.visible = true;
					bgImage2.visible = false;
					if(showSizeDialog == true)
						sizeDialog.drawNow();

					break;
			}
			drawWorld();
		}
	}
	
}